package ms.unicorn.dsl.eistar.util;

import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

import ms.unicorn.dsl.eistar.util.Predicates.Predicate;
import eistar.Entity;
import eistar.Relation;

public class EistarUtil {
	
	public static int OUT_CLOSURE = 1;
	public static int IN_CLOSURE = 2;
	public static int BI_CLOSURE = 3;
	public static int REFER_TO_ME = 4;
	
	/**
	 * Find a closure collection of a given set of entities within another set of entities.
	 * @param <T>
	 * @param space The space in which contain the closure
	 * @param initial The initial entity set
	 * @param closure The output closure set
	 */
	public static void findClosure(Collection<? extends Entity> space, Collection<? extends Entity> initial, Collection<Entity> closure) {
		findClosure(space, initial, closure, BI_CLOSURE, null);			
	}
	
	public static void findClosure(Collection<? extends Entity> space, Collection<? extends Entity> initial, 
			Collection<Entity> closure, int breadingType, Predicate<Entity> pred) {
		for(Entity i: initial)
			if (pred == null || pred.validate(i))
				closure.add(unwrapEntity(i));
		
		Queue<Entity> queue = new ArrayBlockingQueue<Entity>(space.size());
		queue.addAll(initial);
		
		int roleForRelation = REFER_TO_ME, roleForEntity = 0;
		
		if ((breadingType & OUT_CLOSURE) > 0) { 
			roleForRelation |= Entity.TARGET;
			roleForEntity |=  Entity.SOURCE;
		}
		if ((breadingType & IN_CLOSURE) > 0) {
			roleForRelation |= Entity.SOURCE;
			roleForEntity |= Entity.TARGET;
		}
		
		if ((breadingType & REFER_TO_ME) > 0) roleForEntity |=  Entity.TARGET | Entity.SOURCE;
		
		while (!queue.isEmpty()) {
			Entity e = queue.remove();
			Entity unwrappedEntity = unwrapEntity(e);
			for(Entity entity: space) {
				if (!closure.contains(entity) && (pred == null || pred.validate(entity))) {
					boolean ref =  entity.isReferredTo(unwrappedEntity, unwrappedEntity instanceof Relation ? 
										roleForRelation : roleForEntity);
					if (ref) {
						closure.add(entity);
						queue.add(entity);
					}
				}
			}
		}
	}
	
	public static Entity unwrapEntity(Entity e) {
		if (e != null)
			while ((e instanceof IEntityWrapper)) e = ((IEntityWrapper) e).unwrapEntity();
		return e;
	}

}
